package org.sercho.masp.notification.settings;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * The object-representation of the notification-settings, which includes the
 * registered message-categories, along with their times of notification and
 * related urgencies, and the stored urgencies for these times of the users.
 * 
 * @author Benny
 * 
 */
@XmlRootElement(namespace = "http://dabens.de")
public class NotificationSettings {
	/**
	 * The registered {@link MessageCategory}-objects mapped to their IDs.
	 */
	private Map<String, MessageCategory> messageCategories = new HashMap<String, MessageCategory>();
	/**
	 * The {@link User}-objects mapped to the user-IDs.
	 */
	private Map<String, User> users = new HashMap<String, User>();

	/**
	 * Returns all registered {@link MessageCategory}-objects.
	 * 
	 * @return a list of all registered message-categories
	 */
	@XmlElement(name = "messageCategory")
	public List<MessageCategory> getMessageCategories() {
		List<MessageCategory> msgList = new ArrayList<MessageCategory>();
		Set<String> keys = this.messageCategories.keySet();
		for (String key : keys) {
			MessageCategory msg = this.messageCategories.get(key);
			msg.id = key;
			msgList.add(msg);
		}
		return msgList;
	}

	/**
	 * Adds {@link MessageCategory}s to the registered ones. When a category
	 * already exists for an ID, it will be overwritten.
	 * 
	 * @param messageCategories
	 *            a list of {@link MessageCategory}s to be added.
	 */
	public void setMessageCategories(List<MessageCategory> messageCategories) {
		Map<String, MessageCategory> msgMap = new HashMap<String, MessageCategory>();
		for (MessageCategory msg : messageCategories) {
			msgMap.put(msg.id, msg);
		}
		this.messageCategories = msgMap;
	}

	/**
	 * Returns all registered {@link User}-objects.
	 * 
	 * @return a list of all registered users
	 */
	@XmlElement(name = "user")
	public List<User> getUsers() {
		List<User> userList = new ArrayList<User>();
		Set<String> keys = this.users.keySet();
		for (String key : keys) {
			User user = this.users.get(key);
			user.id = key;
			userList.add(user);
		}
		return userList;
	}

	/**
	 * Adds {@link User}s to the existing ones. When a User already exists for
	 * an ID, it will be overwritten.
	 * 
	 * @param users
	 *            a list of {@link User}s to be added.
	 */
	public void setUsers(List<User> users) {
		Map<String, User> userMap = new HashMap<String, User>();
		for (User user : users) {
			userMap.put(user.id, user);
		}
		this.users = userMap;
	}

	/**
	 * Class for representing message-categories. An instance of this class
	 * contains the category-ID, the category-name and a list of
	 * {@link TimeOfNotification}-objects.
	 * 
	 * @author Benny
	 * 
	 */
	public static class MessageCategory {
		/**
		 * The ID of the {@link MessageCategory}.
		 */
		public String id;
		/**
		 * The name of the {@link MessageCategory}.
		 */
		public String categoryName;
		/**
		 * A {@link List} of {@link TimeOfNotification}s.
		 */
		@XmlElement(name = "timeOfNotification")
		public List<TimeOfNotification> urgencyList;
	}

	/**
	 * Class for representing user-specific urgencies. An instance of this class
	 * contains the ID of the user, whose urgencies are hold in this instance,
	 * and a list of {@link MessageCategory}-objects. Only those
	 * {@link MessageCategory}-objects are present in this list, for which the
	 * user has saved own urgencies.
	 * 
	 * @author Benny
	 * 
	 */
	public static class User {
		/**
		 * The ID of the {@link User}.
		 */
		public String id;
		/**
		 * A mapping of {@link MessageCategory}-IDs to {@link MessageCategory}.
		 */
		Map<String, MessageCategory> messageCategories;

		/**
		 * Returns all {@link MessageCategory}-objects for which the user has
		 * saved urgencies.
		 * 
		 * @return a list of all message-categories for which urgencies are
		 *         saved for this user
		 */
		@XmlElement(name = "messageCategory")
		public List<MessageCategory> getMessageCategories() {
			List<MessageCategory> msgList = new ArrayList<MessageCategory>();
			if (this.messageCategories == null) {
				this.messageCategories = new HashMap<String, MessageCategory>();
			}
			Set<String> keys = this.messageCategories.keySet();
			for (String key : keys) {
				MessageCategory msg = this.messageCategories.get(key);
				msg.id = key;
				msgList.add(msg);
			}
			return msgList;
		}

		/**
		 * Adds {@link MessageCategory}s to the already saved ones. When a
		 * category already exists for an ID, it will be overwritten.
		 * 
		 * @param messageCategories
		 *            a list of {@link MessageCategory}s to be added.
		 */
		public void setMessageCategories(List<MessageCategory> messageCategories) {
			Map<String, MessageCategory> msgMap = new HashMap<String, MessageCategory>();
			for (MessageCategory msg : messageCategories) {
				msgMap.put(msg.id, msg);
			}
			this.messageCategories = msgMap;
		}
	}

	/**
	 * Class for representing times of notification. An instance of this class
	 * contains the time-ID, the time-description and the urgency for this time.
	 * 
	 * @author Benny
	 * 
	 */
	public static class TimeOfNotification {
		/**
		 * The ID of the {@link TimeOfNotification}.
		 */
		public String id;
		/**
		 * The description of the {@link TimeOfNotification}.
		 */
		public String description;
		/**
		 * The urgency of the {@link TimeOfNotification}.
		 */
		public int urgency;
	}

	/**
	 * Registers or changes a {@link MessageCategory}.
	 * 
	 * @param msg
	 *            The {@link MessageCategory} to add or update.
	 */
	public void addMessageCategory(MessageCategory msg) {
		this.messageCategories.put(msg.id, msg);
	}

	/**
	 * Saves or changes an user-urgency for a time of notification of a specific
	 * message.
	 * 
	 * @param userId
	 *            the ID of the user to save the urgency for
	 * @param messageId
	 *            the unique ID of the message-category
	 * @param timeId
	 *            the ID of the time of notification
	 * @param urgency
	 *            the urgency for this time
	 */
	public void addUserUrgency(String userId, String messageId, String timeId,
			int urgency) {
		User user = this.users.get(userId);
		if (user == null) {
			user = new User();
			user.id = userId;
			this.users.put(userId, user);
		}
		if (user.messageCategories == null) {
			user.messageCategories = new HashMap<String, MessageCategory>();
		}
		MessageCategory msg = user.messageCategories.get(messageId);
		if (msg == null) {
			msg = new MessageCategory();
			msg.id = messageId;
			user.messageCategories.put(messageId, msg);
		}
		if (msg.urgencyList == null) {
			msg.urgencyList = new ArrayList<TimeOfNotification>();
		}
		TimeOfNotification time = new TimeOfNotification();
		time.id = timeId;
		time.urgency = urgency;
		for (TimeOfNotification timeTmp : msg.urgencyList) {
			if (timeTmp.id.equalsIgnoreCase(timeId)) {
				msg.urgencyList.remove(timeTmp);
				break;
			}
		}
		msg.urgencyList.add(time);
	}

	/**
	 * Retrieves the default-urgency for the given message-category and time of
	 * notification, which was registered by the message-source.
	 * 
	 * @param messageId
	 *            the unique ID of the message-category
	 * @param timeOfNotificationId
	 *            the ID of the time of notification, to retrieve the urgency
	 *            for
	 * @return the default-urgency
	 */
	public int getDefaultUrgency(String messageId, String timeOfNotificationId) {
		int urgency = 3;
		MessageCategory msg = this.messageCategories.get(messageId);
		if (msg != null) {
			for (TimeOfNotification time : msg.urgencyList) {
				if (time.id.equalsIgnoreCase(timeOfNotificationId)) {
					urgency = time.urgency;
					break;
				}
			}
		}
		return urgency;
	}

	/**
	 * Retrieves the user-urgency for the given message-category and time of
	 * notification.
	 * 
	 * @param userId
	 *            the ID of the user, whose urgency shall be retrieved
	 * @param messageId
	 *            the unique ID of the message-category
	 * @param timeOfNotificationId
	 *            the ID of the time of notification, to retrieve the urgency
	 *            for
	 * @return the user-urgency
	 */
	public int getUserUrgency(String userId, String messageId,
			String timeOfNotificationId) {
		int urgency = 0;
		User user = this.users.get(userId);
		if (user != null) {
			MessageCategory msg = user.messageCategories.get(messageId);
			if (msg != null) {
				for (TimeOfNotification time : msg.urgencyList) {
					if (time.id.equalsIgnoreCase(timeOfNotificationId)) {
						urgency = time.urgency;
						break;
					}
				}
			}
		}
		return urgency;
	}
}
